﻿/*
 *  Copyright 2010 David Hawkes, daveh@cadlink.com
 *  Subject to the Apache License version 2.0 at http://www.apache.org/licenses/
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Reflection;
using DynamicAccess;
using IronPython.Hosting;
using IronRuby;
using IronRuby.Hosting;
using Microsoft.Scripting;

namespace DynScript
{
  class Program
  {
    static void Main(string[] args)
    {
      Console.WriteLine("Tests started");
      try {
        ExpandoTesting();
        RubyTesting();
        PythonTesting();
        ManagedCpp.CppTesting.Test();
        OtherTesting();
      } catch(Exception e) {
        Console.WriteLine(string.Format("Tests failed with exception:\n{0}", e.ToString()));
      }
      Console.WriteLine("Tests completed, Press any key to continue");
      Console.ReadKey();
    }

    static void ExpandoTesting()
    {
      dynamic expando = new ExpandoObject();
      IDictionary<string, object> dict = (IDictionary<string, object>)expando;
      expando.t1 = "T1";
      expando.t9 = new ExpandoObject();
      expando.t9.t5 = "T9T5";
      var t1 = Accessors.GetMember(expando, "t1");
      if(t1 != expando.t1)
        Console.WriteLine("ExpandoObject error: GetMember");
      Accessors.SetMember(expando, "t2", "T2");
      if("T2" != expando.t2)
        Console.WriteLine("ExpandoObject error: SetMember");
      Accessors.DeleteMember(expando, "t2");
      if(dict.ContainsKey("t2"))
        Console.WriteLine("ExpandoObject error: DeleteMember");
    }

    static string PyTest = @"
import sys
import DynScript

class PyTest(object):
  def __init__(self, p1=None):
    self.p1 = p1
  @staticmethod
  def StaticTest1():
    return ""Test""
  @staticmethod
  def StaticTest2(x):
    return 6*x
  def InstTest1(self):
    return self.p1
";

    static void PythonTesting()
    {
      var env = Python.CreateRuntime();
      var eng = Python.GetEngine(env);
      env.LoadAssembly(Assembly.GetExecutingAssembly());
      var scope = env.CreateScope();
      var ss = eng.CreateScriptSourceFromString(PyTest, SourceCodeKind.Statements);
      ss.Execute(scope);
      var builtins = scope.GetVariable("__builtins__");
      var hasattr = builtins["hasattr"];
      var PyTestType = scope.GetVariable("PyTest");
      // Create instance - somewhat redundant with python as we could just call the type directly: PyTestType("xx")
      var PyTestInstance = Accessors.CreateInstance(PyTestType, "xx");
      // Get a member
      var prop = Accessors.GetMember(PyTestInstance, "p1");
      if(prop != PyTestInstance.p1)
        Console.WriteLine("Python error: GetMember");
      // Set a member
      Accessors.SetMember(PyTestInstance, "p2", "xxx");
      if("xxx" != PyTestInstance.p2)
        Console.WriteLine("Python error: SetMember");
      // Delete a member
      Accessors.DeleteMember(PyTestInstance, "p2");
      if(hasattr(PyTestInstance, "p2") == builtins["True"])
        Console.WriteLine("Python error: DeleteMember");
      // Call a method/member
      var n = Accessors.CallMember(PyTestInstance, "InstTest1");
      if(n != PyTestInstance.InstTest1())
        Console.WriteLine("Python error: CallMember");
      // Create a python dictionary - again we could just use: builtins["dict"]()
      dynamic PyDict = Accessors.CreateInstance(Accessors.GetIndex(builtins, "dict"));
      // Set an index
      Accessors.SetIndex(PyDict, "zzz", 42);
      if(PyDict["zzz"] != 42)
        Console.WriteLine("Python error: SetIndex");
      // Delete an index
      Accessors.DeleteIndex(PyDict, "zzz");
      if(PyDict.has_key("zzz") == builtins["True"])
        Console.WriteLine("Python error: DeleteIndex");
    }

    static string RubyTest = @"
require 'DynScript'

class RubyTest
  def initialize(p1=nil)
    @p1 = p1
  end
  def self.StaticTest1
    ""Test""
  end
  def self.StaticTest2(x)
    6*x
  end
  def InstTest1
    return @p1
  end
  attr_accessor :p1
end
";

    static void RubyTesting()
    {
      var env = Ruby.CreateRuntime();
      var eng = Ruby.GetEngine(env);
      env.LoadAssembly(Assembly.GetExecutingAssembly());
      var scope = env.CreateScope();
      var ss = eng.CreateScriptSourceFromString(RubyTest, SourceCodeKind.Statements);
      ss.Execute(scope);
      var RubyTestType = eng.Execute("RubyTest", scope);
      // Create an instance
      var RubyTestInstance = Accessors.CreateInstance(RubyTestType, "xyzzy");
      // Call an instance member
      var val = Accessors.CallMember(RubyTestInstance, "InstTest1");
      if(val != RubyTestInstance.InstTest1())
        Console.WriteLine("Ruby error: CallMember");
      // Call a static member
      var n = Accessors.CallMember(RubyTestType, "StaticTest2", 10);
      if(n != RubyTestType.StaticTest2(10))
        Console.WriteLine("Ruby error: CallMember");
      // Get a member
      var p1 = Accessors.GetMember(RubyTestInstance, "p1");
      if(p1 != RubyTestInstance.p1)
        Console.WriteLine("Ruby error: GetMember");
      Accessors.SetMember(RubyTestInstance, "p1", 42);
      if(RubyTestInstance.p1 != 42)
        Console.WriteLine("Ruby error: SetMember");
      var RubyArray = eng.Execute("Array(10)", scope);
      // Set an index
      Accessors.SetIndex(RubyArray, 2, 42);
      if(RubyArray[2] != 42)
        Console.WriteLine("Ruby error: SetIndex");
      // Get an index
      n = Accessors.GetIndex(RubyArray, 2);
      if(RubyArray[2] != n)
        Console.WriteLine("Ruby error: GetIndex");
      var RubyHash = eng.Execute("Hash.new", scope);
      Accessors.SetIndex(RubyHash, "p5", "xyzzy");
      if(RubyHash["p5"] != "xyzzy")
        Console.WriteLine("Ruby error: SetIndex");
      Accessors.DeleteIndex(RubyHash, "p5");
      IDictionary dict = RubyHash as IDictionary;
      if(dict.Contains("p5"))
        Console.WriteLine("Ruby error: DeleteIndex");
    }

    struct OT1
    {
      public int v1;
      public OT1(int v1) { this.v1 = v1; }
    }

    static void OtherTesting()
    {
      // allocate a structure
      dynamic ot1 = new OT1(5);
      // test for crash when using value types
      Accessors.SetMember(ot1, "v1", 6);
    }
  }

  /// <summary>
  ///  Simple class to make namespace visible for testing
  /// </summary>
  public class PyTest
  {
  }
}
